/*
    Dominate 3D - A cube-based game of life

    Copyright (C) 2007, Craig H. Miller (kidmosey@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MATRIX_H__
#define __MATRIX_H__

#include "vector.h"

/** A 3x3 matrix.
This is a more optimized version of a 4x4 matrix. In memory, it sits
like a 4x4 matrix due to the 4 pad variables which are set to 0,0,0,1.

There are 4 vectors which can be useful for geometric operations. Think of the
'a' vector as pointing to the object's right, 'b' pointing to the object's top,
and 'c' pointing to the objects back. Usually 'a', 'b', and 'c' will be unit
length and perpendicular to each other. 'd' represents the object's position.

There are various functions for creating standard matrices (rotation, translation,
scale...) as well as some other algebra functions (inverse, transpose...).

Because the CMatrix sits in memory the same way as a 4x4 matrix, it can be
passed directly to OpenGL with the glMultMatrixf(mtx) or glLoadMatrixf(mtx)
commands. The CMatrix specifies an implicit float* cast operation to make
this work.
*/
class CMatrix {
public:
	/// Identity constructor
	CMatrix()
	{
		Identity();
		pad0=pad1=pad2=0.0f; pad3=1.0f;
	}

	/// constructor
	CMatrix(float ax,float ay,float az,float bx,float by,float bz,
		float cx,float cy,float cz,float dx,float dy,float dz);

	/// Set this matrix as an identity matrix
	void Identity();

	/// return the product of this matrix and another matrix
	CMatrix  operator*  (const CMatrix &rhs) const;

	/// multiply this matrix by another matrix
	CMatrix &operator*= (const CMatrix &rhs);

	void Dot(const CMatrix &m,const CMatrix &n);	// this = m (dot) n

	/** Transform a vector using this matrix.
	 *	\param in Vector to transform
	 *	\param out Transformed vector.
	 */
	void Transform(const CVector3D &in,CVector3D &out) const;

	/** Transform a vector using this matrix.
	 *	\param in Vector to transform
	 *	\param out Transformed vector.
	 */
	void Transform3x3(const CVector3D &in,CVector3D &out) const;

	/** Make a rotation matrix around the X axis.
	 *	\param t An angle in radians
	 */
	void MakeRotateX(float t);

	/** Make a rotation matrix around the Y axis.
	 *	\param t An angle in radians
	 */
	void MakeRotateY(float t);

	/** Make a rotation matrix around the Z axis.
	 *	\param t An angle in radians
	 */
	void MakeRotateZ(float t);

	/** Make a matrix based on a quaternion
	 *	\param v A normalized axis.
	 *	\param t The rotation angle in radians.
	 */
	void MakeRotateUnitAxis(const CVector3D &v,float t);

	/// Make the matrix a scalar matrix on x,y,z
	void MakeScale(float x,float y,float z);

	/// Make the matrix a scalar matrix
	void MakeScale(const CVector3D &v)				{MakeScale(v.x,v.y,v.z);}

	/// Make the matrix a uniform scalar matrix
	void MakeScale(float s)							{MakeScale(s,s,s);}

	/// Make the matrix a translation matrix
	void MakeTranslate(float x,float y,float z)
	{
		Identity();
		d = CVector3D(x, y, z);
	}

	/// Make the matrix a translation matrix
	void MakeTranslate(const CVector3D &v)			{Identity(); d=v;}

	/// Euler angles
	enum {EULER_XYZ,EULER_XZY,EULER_YXZ,EULER_YZX,EULER_ZXY,EULER_ZYX};

	/** Make the matrix a rotation matrix based on euler angles.
	 *	\param euler	The x,y,z angles of rotation
	 *	\param order	The order in which to do the rotations.
	 */
	void FromEulers(const CVector3D &euler,int order);

	/** Convert the rotation of this matrix to euler angles.
	 *	\param euler	The resulting angles
	 *	\param order	The order in which to do the rotations.
	 */
	void ToEulers(CVector3D &euler,int order);

	/// Full Inverse (expensive)
	bool Inverse();

	/// Fast inverse (only works on ORTHONORMAL matrices).
	void FastInverse();

	/** Transpose the matrix.
	 *	only modifies 3x3 portion (doesn't change d)
	 */
	void Transpose();

	/** Make this matrix a LookAt matrix.
	 *	\param from	Camera vector
	 *	\param to	LookAt vector
	 *
	 *	The up vector is assumed to be the Y axis.
	 *
	 */
	void LookAt(const CVector3D &from,const CVector3D &to);

	void PolarView(float dist,float azm,float inc,float twst=0);

	float Determinant3x3() const;
	CVector3D &operator[](int i)						{return *((CVector3D*)&(((float*)this)[i*4]));}	// yuck!
	operator float*()								{return (float*)this;}

	// Static matrices
	static CMatrix IDENTITY;

public:
	CVector3D a; float pad0;
	CVector3D b; float pad1;
	CVector3D c; float pad2;
	CVector3D d; float pad3;
};

#endif // __MATRIX_H__
